//
//  JFCocoaAsyncSocketInstance.m
//  JFBase
//
//  Created by Alan Wang on 2018/11/22.
//  Copyright © 2018 Alan Wang. All rights reserved.
//

#import "JFCocoaAsyncSocketInstance.h"
#import "JFStringHelper.h"
#import "JFLogHelper.h"
#import "JFSocketResultVo.h"
#import <Foundation/Foundation.h>
#import "JFSocketDataParserHelper.h"

#if __has_include(<CocoaAsyncSocket/GCDAsyncSocket.h>)
#import <CocoaAsyncSocket/GCDAsyncSocket.h>
#else
#import "GCDAsyncSocket.h"
#endif

@interface JFCocoaAsyncSocketInstance()<GCDAsyncSocketDelegate>


@end
@implementation JFCocoaAsyncSocketInstance{
    //缓存Socket
    NSMutableDictionary *_requestQueue;
    //缓存baseUri 的解析类
    NSMutableDictionary *_dataParserQueue;
    //缓存每个功能号的回调
    NSMutableDictionary *_backQueue;
    //缓存那些socket在重连
    NSMutableDictionary *_reconnectQueue;
    //缓存socket未连接 等待连接成功后发送的数据
    NSMutableArray * _bakData;
    //业务层心跳
    NSTimer *  _connectTimer;
}

+(instancetype )shareInstance
{
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [[self alloc]init];
    });
    return instance;
}

-(id)init
{
    self = [super init];
    if (self)
    {
        _requestQueue=[[NSMutableDictionary alloc] init];
        _dataParserQueue=[[NSMutableDictionary alloc] init];
        _backQueue = [[NSMutableDictionary alloc] init];
        _bakData = [[NSMutableArray alloc] init];
        _reconnectQueue = [[NSMutableDictionary alloc] init];
        //开启定时器
        _connectTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(longConnectToSocket:) userInfo:nil repeats:YES];
        [_connectTimer fire];
    }
    return self;
}

-(void)invoke:(JFRequestVo *)reqParamVo callBack:(JFCallBack)callback{
    [super invoke:reqParamVo callBack:callback];
    
    GCDAsyncSocket * AsyncSocket = _requestQueue[reqParamVo.baseUri];
    if(!AsyncSocket){
        NSString * url = reqParamVo.baseUri;
        if([JFStringHelper isEmpty:url]){
            return;
        }
        [self connectToSocket:url reqParamVo:reqParamVo];
    }
    NSString * dataParser = reqParamVo.dataparserServiceName;
    if(dataParser){
        _dataParserQueue[reqParamVo.baseUri] = dataParser;
    }else{
        _dataParserQueue[reqParamVo.baseUri] = @"JFSocketDataParserHelper";
    }
    
    NSMutableDictionary * dict = _backQueue[reqParamVo.baseUri];
    if(!dict){
        dict = [[NSMutableDictionary alloc] init];
        if(callback){
            dict[reqParamVo.flowNo] = callback;
        }
        _backQueue[reqParamVo.baseUri] = dict;
    }else{
        if(callback){
            dict[reqParamVo.flowNo] = callback;
        }
    }
    //把需要发送的数据缓存起来
    if(AsyncSocket){
        JFSocketResultVo * result = nil;
        Class class = NSClassFromString(dataParser);
        if([class respondsToSelector:@selector(dataParser:data:dataParserType:)] && reqParamVo.reqParam){
            result = [class dataParser:reqParamVo.baseUri data:reqParamVo.reqParam  dataParserType:DataParserType_Socket];
        }
        [self dealWithResult:result socket:AsyncSocket];
    }else{
        [_bakData addObject:reqParamVo];
    }
}
-(void)sendData:(JFRequestVo *)reqParamVo socket:(GCDAsyncSocket*)socket{
    if(!reqParamVo.reqParam){
        JFSocketResultVo * result = nil;
        NSString * dataParserClass = _dataParserQueue[reqParamVo.baseUri];
        Class class = NSClassFromString(dataParserClass);
        if([class respondsToSelector:@selector(dataParser:data:dataParserType:)]){
            result = [class dataParser:reqParamVo.baseUri data:reqParamVo.reqParam  dataParserType:DataParserType_Socket];
        }
        [self dealWithResult:result socket:socket];
    }

}
-(void)connectToSocket:(NSString*)url reqParamVo:(JFRequestVo *)reqParamVo{
    NSString * urlCopy = [url copy];
    NSArray * urlArr = [urlCopy componentsSeparatedByString:@":"];
    GCDAsyncSocket * AsyncSocket;
    if(urlArr.count>1){
        AsyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        NSError * error=nil;
        [AsyncSocket connectToHost:urlArr[0] onPort:[urlArr[1] integerValue] withTimeout:reqParamVo.timeOut error:&error];
        AsyncSocket.userData = url;
        if (error) {
            NSString * logStr = [NSString stringWithFormat:@"Socket url:%@,port:%@ 启动失败",urlArr[0],urlArr[1]];
            if(reqParamVo.isDebug){
                [JFLogHelper stringWriteLogToConsole:logStr];
            }
        } else {
            NSString * logStr = [NSString stringWithFormat:@"Socket url:%@,port:%@ 启动成功",urlArr[0],urlArr[1]];
            if(reqParamVo.isDebug){
                [JFLogHelper stringWriteLogToConsole:logStr];
            }
        }
    }else{
        return;
    }
}
#pragma mark--辅助方法
-(void)longConnectToSocket:(NSTimer*)time{
    NSArray * allReconnectSocket = _reconnectQueue.allKeys;
    JFSocketResultVo * result = nil;
    for (NSString * key  in _dataParserQueue.allKeys) {
        if(![allReconnectSocket containsObject:key]){//不在重连发送心跳
            NSString * dataParserClass = _dataParserQueue[key];
            Class class = NSClassFromString(dataParserClass);
            if([class respondsToSelector:@selector(heartbeat:)]){
                 result = [class heartbeat:key];
            }
            GCDAsyncSocket * socket = _requestQueue[key];
            [self dealWithResult:result socket:socket];
        }
    }
}
-(void)cancelAllRequest{
    if (_requestQueue) {
        //取消所有的请求
        for (NSString* baseUrl in _requestQueue.allKeys) {
            [self removeObjectByKey:baseUrl];
        }
        [_requestQueue removeAllObjects];
        [_backQueue removeAllObjects];
        [_dataParserQueue removeAllObjects];
        [_reconnectQueue removeAllObjects];
        [_bakData removeAllObjects];
    }
}

- (void)clearAllRequest{
    [self cancelAllRequest];
}
-(void)clearRequest:(NSString *)flowNo baseUri:(NSString*)url{
    
    NSMutableDictionary * dict = _backQueue[url];
    for (NSString *key in dict.allKeys) {
        if([key isEqualToString:flowNo]){
            [dict removeObjectForKey:key];
            _backQueue[url] = dict;
        }
    }
    //如果没有任何人在监听这个socket 直接移除掉
    if(!dict.allKeys.count){
        [self removeObjectByKey:url];
        [_requestQueue removeObjectForKey:url];
        [_backQueue removeObjectForKey:url];
        [_dataParserQueue removeObjectForKey:url];
        [_reconnectQueue removeObjectForKey:url];
    }
}

-(void)removeObjectByKey:(NSString*)key{
    if(_requestQueue && key){
        GCDAsyncSocket * socket = _requestQueue[key];
        if(socket){
            socket.delegate = nil;
            socket = nil;
        }
        [_requestQueue removeObjectForKey:key];
    }
}
//链接成功
-(void)socket:(GCDAsyncSocket *)socket didConnectToHost:(NSString *)host port:(uint16_t)port
{
    NSString * url = socket.userData;
    if(url&&socket){
        _requestQueue[url] = socket;
        [_reconnectQueue removeObjectForKey:url];
    }
    NSString * dataParserClass = _dataParserQueue[url];
    JFSocketResultVo * result = nil;
    Class class = NSClassFromString(dataParserClass);
    if([class respondsToSelector:@selector(didConnect:)]){
        result = [class didConnect:url];
    }
    [self dealWithResult:result socket:socket];
    

    //把连接上的socket数据发送出去
    if(_bakData&&_bakData.count>0){
        AWWeakSelf(self)
        [_bakData enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            JFRequestVo * reqParamVo = obj;
            if(reqParamVo.reqParam && [url isEqualToString: reqParamVo.baseUri]){//发送数据
                [weakself sendBakData:reqParamVo];
            }
        }];
    }
}
-(void)sendBakData:(JFRequestVo*)reqParamVo{
    GCDAsyncSocket * socket = _requestQueue[reqParamVo.baseUri];
    if(socket){
        [self sendData:reqParamVo socket:socket];
        [self removeBakData:reqParamVo];
    }
}
-(void)removeBakData:(JFRequestVo*)reqParamVo{
    if(reqParamVo){
        [_bakData removeObject:reqParamVo];
    }
}

-(void)dealWithResult:(JFSocketResultVo*)result socket:(GCDAsyncSocket*)socket{
  
    if(result){
        switch (result.actionType) {
            case DataParserActionType_Read:{
                [self readData:result socket:socket];
            }
                break;
            case DataParserActionType_Write:{
                if(socket){
                  [socket writeData:result.data withTimeout:result.timeOut tag:result.tag];
                }
            }break;
            case DataParserActionType_ReadWrite:{
                if(socket){
                    [socket writeData:result.data withTimeout:result.timeOut tag:result.tag];
                }
                [self readData:result socket:socket];
            }break;
            //完成回调
            case DataParserActionType_Complete:{
                NSString * url  = socket.userData;
                NSMutableDictionary * dict = _backQueue[url];
                for (NSString *key in dict.allKeys) {
                    JFCallBack callback = dict[key];
                    if(callback){
                        callback(result);
                    }
                }
            }break;
            default:
                break;
        }
    }
}
-(void)readData:(JFSocketResultVo*)result socket:(GCDAsyncSocket*)socket{
    if(!socket){
        return;
    }
    switch (result.readType) {
        case DataReadType_Length:
        {
          [socket readDataToLength:result.length withTimeout:result.timeOut tag:result.tag];
        }
            break;
        case DataReadType_EndChar:{
            [socket readDataToData:result.data withTimeout:result.timeOut tag:result.tag];
        }break;
        default:
            [socket readDataWithTimeout:result.timeOut tag:result.tag];
            break;
    }
}

//链接失败
- (void)socketDidDisconnect:(GCDAsyncSocket *)socket withError:(NSError *)err{
    NSString * url = socket.userData;
    NSNumber * number = _reconnectQueue[url];
    
    AWWeakSelf(self)
    double delayInSeconds = 1.0;
    if(number){//
        NSInteger reconnectNumber = [number integerValue];
        reconnectNumber++;
        _reconnectQueue[url] = @(reconnectNumber);
        if(reconnectNumber == 5){
            delayInSeconds = 5.0;
        }
        if(reconnectNumber>5){//重连超过五次就移除
            if(url){
                [_requestQueue removeObjectForKey:url];
                [_reconnectQueue removeObjectForKey:url];
                if(![socket isDisconnected])
                {
                    [socket disconnect];
                    socket.delegate = nil;
                }
            }
            return;
        }
    }else{
        if(url){
             _reconnectQueue[url] = @(1);
        }  
    }
    
    if(![socket isDisconnected])
    {
        [socket disconnect];
        socket.delegate = nil;
    }

    dispatch_time_t delayInNanoSeconds =dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_after(delayInNanoSeconds, concurrentQueue, ^(void){
        
        dispatch_async(dispatch_get_main_queue(), ^{
            JFRequestVo * req = [[JFRequestVo alloc] init];
            req.isDebug = NO;
            req.timeOut = 10;
            [weakself connectToSocket:url reqParamVo:req];
        });
    });
}

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
    NSString * url = sock.userData;
    NSString * dataParserClass = _dataParserQueue[url];
    Class class = NSClassFromString(dataParserClass);
    JFSocketResultVo * result = nil;
    if([class respondsToSelector:@selector(dataParser:data:dataParserType:withTag:)]){
        result = [class dataParser:url data:data dataParserType:DataParserType_Socket withTag:tag];
    }
    [self dealWithResult:result socket:sock];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
    
}
@end
